/*******************************************************************************
 * Copyright 2020 huanggefan.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package action

import (
	"fmt"
	"gitee.com/WisdomClassroom/core/models"
	"gitee.com/WisdomClassroom/resource/global"
	"gitee.com/WisdomClassroom/resource/tools"
	"github.com/jinzhu/gorm"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"time"
)

type FileInfo struct {
	FileUUID    string
	FileType    int
	FileName    string
	PutTime     int64
	PutUserUUID string
	ChapterUUID string
}

func SaveFile(fileData []byte, fileInfo *FileInfo) (err error) {
	filePrefix := tools.GetFileMD5Prefix(fileData, fileInfo.FileName, fileInfo.PutUserUUID)
	fileSHA := tools.GetFileSHA(fileData)

	resourceModel := &models.ResourceModel{
		Model:       models.NewModel(),
		Prefix:      filePrefix,
		StoreName:   fileSHA,
		FileName:    fileInfo.FileName,
		FileType:    fileInfo.FileType,
		PutUserUUID: fileInfo.PutUserUUID,
	}
	resourceModel.UUID = fileInfo.FileUUID
	resourceChapterModel := &models.ResourceChapterBindModel{
		Model:        models.NewModel(),
		ResourceUUID: resourceModel.UUID,
		ChapterUUID:  fileInfo.ChapterUUID,
	}

	storePrefix := fmt.Sprintf("%s/%s/%s",
		string(resourceModel.Prefix[0]),
		string(resourceModel.Prefix[1]),
		string(resourceModel.Prefix[2]))
	storePath := path.Join(global.Dir, storePrefix, fileSHA)
	_ = os.MkdirAll(path.Join(global.Dir, storePrefix), 0777)

	tx := global.DB.Begin()
	err = func() error {
		err = tx.Create(resourceModel).Error
		if err != nil {
			return err
		}
		err = tx.Create(resourceChapterModel).Error
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(storePath, fileData, 0777)
		if err != nil {
			return err
		}
		return nil
	}()
	if err != nil {
		tx.Rollback()
	} else {
		tx.Commit()
	}

	return err
}

func GetFile(fileUUID string) (fileData []byte, fileInfo *FileInfo, err error) {
	fileData = make([]byte, 0, 1024*1024*10)
	resourceModel := &models.ResourceModel{}

	err = global.DB.
		Where("uuid = ?", fileUUID).
		Not("deleted", true).
		First(&resourceModel).Error
	if err != nil {
		global.Logger.Warning("Read database Error: " + err.Error())
		return []byte{}, nil, err
	}

	storePrefix := fmt.Sprintf("%s/%s/%s",
		string(resourceModel.Prefix[0]),
		string(resourceModel.Prefix[1]),
		string(resourceModel.Prefix[2]))
	storePath := path.Join(global.Dir, storePrefix, resourceModel.StoreName)
	fileData, err = ioutil.ReadFile(storePath)
	if err != nil {
		global.Logger.Warning("Read File Error: " + err.Error())
		return []byte{}, nil, err
	}

	fileInfo = &FileInfo{
		FileUUID:    resourceModel.UUID,
		FileType:    resourceModel.FileType,
		FileName:    resourceModel.FileName,
		PutTime:     resourceModel.CreationTime,
		PutUserUUID: resourceModel.PutUserUUID,
	}
	return fileData, fileInfo, nil
}

func DeleteFile(fileUUID string) (err error) {
	resourceModel := &models.ResourceModel{}
	resourceChapterModel := &models.ResourceChapterBindModel{}

	now := time.Now().Unix()

	resourceModel.Deleted = true
	resourceChapterModel.Deleted = true
	resourceModel.DeleteTime = now
	resourceChapterModel.DeleteTime = now

	tx := global.DB.Begin()
	err = func() error {
		err = global.DB.Model(&models.ResourceModel{}).
			Where("uuid = ?", fileUUID).Not("deleted", true).
			Updates(resourceModel).Error
		if err != nil {
			global.Logger.Warning("Delete ResourceModel error: " + err.Error())
			return err
		}

		err = global.DB.Model(&models.ResourceChapterBindModel{}).
			Where("resource_uuid = ?", fileUUID).Not("deleted", true).
			Updates(resourceChapterModel).Error
		if err != nil {
			global.Logger.Warning("Delete ResourceChapterBindModel error: " + err.Error())
			return err
		}
		return nil
	}()
	if err != nil {
		tx.Rollback()
	} else {
		tx.Commit()
	}

	return err
}

func ListFile(chapterUUID string) (listInfo []*FileInfo, err error) {
	listInfo = make([]*FileInfo, 0, 4)
	dbResult := make([]*models.ResourceChapterBindModel, 0, 4)

	err = global.DB.
		Where("chapter_uuid = ?", chapterUUID).
		Not("deleted", true).
		Find(&dbResult).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		global.Logger.Warning("Find ResourceChapterBindModel Error: " + err.Error())
		return []*FileInfo{}, err
	}

	for _, result := range dbResult {
		resourceModel := &models.ResourceModel{}
		err = global.DB.
			Where("uuid = ?", result.ResourceUUID).
			Not("deleted", true).
			First(&resourceModel).Error
		if err != nil {
			global.Logger.Warning("Read database Error: " + err.Error())
			continue
		}

		i := &FileInfo{
			FileUUID:    resourceModel.UUID,
			FileType:    resourceModel.FileType,
			FileName:    resourceModel.FileName,
			PutTime:     resourceModel.CreationTime,
			PutUserUUID: resourceModel.PutUserUUID,
			ChapterUUID: chapterUUID,
		}
		listInfo = append(listInfo, i)
	}

	return listInfo, nil
}

func CheckChapter(req *http.Request) (string, bool) {
	chapterUUID := req.PostFormValue(global.POSTFormChapter)
	if chapterUUID == "" {
		return "", false
	}

	err := global.DB.Where("uuid = ?", chapterUUID).
		Not("deleted", true).
		First(&models.ChapterModel{}).Error
	if err != nil {
		return "", false
	}

	return chapterUUID, true
}
